#include <xen/perfc.h>
#include <asm/processor.h>
+/*
+ * Obtain the base and limit associated with the given segment selector.
+ * The selector must identify a 32-bit code or data segment. Any segment that
+ * appears to be truncated to not overlap with Xen is assumed to be a truncated
+ * 4GB segment, and the returned limit reflects this.
+ * @seg (IN) : Segment selector to decode.
+ * @base (OUT): Decoded linear base address.
+ * @limit (OUT): Decoded segment limit, in bytes. 0 == unlimited (4GB).
+ */
int get_baselimit(u16 seg, unsigned long *base, unsigned long *limit)
{
struct domain *d = current;
return 0;
}
+/* Turn a segment+offset into a linear address. */
int linearise_address(u16 seg, unsigned long off, unsigned long *linear)
{
unsigned long base, limit;
return 1;
}
+/* Decode Reg field of a ModRM byte: return a pointer into a register block. */
void *decode_reg(struct pt_regs *regs, u8 b)
{
switch ( b & 7 )
* @pseg (IN) : address in pt_regs block of the override segment.
* @regs (IN) : addrress of the the pt_regs block.
*/
+#define DECODE_EA_FAILED 0
+#define DECODE_EA_FIXME 1
+#define DECODE_EA_SUCCESS 2
int decode_effective_address(u8 **ppb, void **preg, void **pmem,
unsigned int *pseg, struct pt_regs *regs)
{
if ( get_user(modrm, pb) )
{
DPRINTK("Fault while extracting modrm byte\n");
- return 0;
+ return DECODE_EA_FAILED;
}
pb++;
if ( rm == 4 )
{
DPRINTK("FIXME: Add decoding for the SIB byte.\n");
- return 0;
+ return DECODE_EA_FIXME;
}
/* Decode Reg and R/M fields. */
if ( get_user(disp32, (u32 *)pb) )
{
DPRINTK("Fault while extracting <disp8>.\n");
- return 0;
+ return DECODE_EA_FAILED;
}
pb += 4;
}
if ( get_user(disp8, pb) )
{
DPRINTK("Fault while extracting <disp8>.\n");
- return 0;
+ return DECODE_EA_FAILED;
}
pb++;
disp32 = (disp8 & 0x80) ? (disp8 | ~0xff) : disp8;;
if ( get_user(disp32, (u32 *)pb) )
{
DPRINTK("Fault while extracting <disp8>.\n");
- return 0;
+ return DECODE_EA_FAILED;
}
pb += 4;
break;
case 3:
DPRINTK("Not a memory operand!\n");
- return 0;
+ return DECODE_EA_FAILED;
}
if ( !get_baselimit((u16)(*pseg), &ea, &limit) )
- return 0;
+ return DECODE_EA_FAILED;
if ( limit != 0 )
{
DPRINTK("Bailing: not a 4GB data segment.\n");
- return 0;
+ return DECODE_EA_FAILED;
}
offset = disp32;
if ( (offset & 0xf0000000) != 0xf0000000 )
{
DPRINTK("Bailing: not a -ve offset into 4GB segment.\n");
- return 0;
+ return DECODE_EA_FAILED;
}
ea += offset;
if ( ea > (PAGE_OFFSET - PAGE_SIZE) )
{
DPRINTK("!!!! DISALLOWING UNSAFE ACCESS !!!!\n");
- return 0;
+ return DECODE_EA_FAILED;
}
*ppb = pb;
*preg = regreg;
*pmem = (void *)ea;
- return 1;
+ return DECODE_EA_SUCCESS;
}
+#define GET_IMM8 \
+ if ( get_user(ib, (u8 *)pb) ) { \
+ DPRINTK("Fault while extracting imm8\n"); \
+ return 0; \
+ } \
+ pb += 1;
+#define GET_IMM16 \
+ if ( get_user(iw, (u8 *)pb) ) { \
+ DPRINTK("Fault while extracting imm16\n"); \
+ return 0; \
+ } \
+ pb += 2;
+#define GET_IMM32 \
+ if ( get_user(il, (u32 *)pb) ) { \
+ DPRINTK("Fault while extracting imm32\n"); \
+ return 0; \
+ } \
+ pb += 4;
+
/*
* Called from the general-protection fault handler to attempt to decode
* and emulate an instruction that depends on 4GB segments. At this point
{
struct domain *d = current;
trap_info_t *ti;
- u8 *eip, *nextbyte, b, mb, rb;
- u16 mw, rw;
- u32 ml, rl, eflags;
- unsigned int *pseg = NULL;
- int i;
- int opsz_override = 0;
- void *reg, *mem;
struct guest_trap_bounce *gtb;
+ u8 *eip; /* ptr to instruction start */
+ u8 *pb, b; /* ptr into instr. / current instr. byte */
+ u8 ib, mb, rb; /* byte operand from imm/register/memory */
+ u16 iw, mw, rw; /* word operand from imm/register/memory */
+ u32 il, ml, rl; /* long operand from imm/register/memory */
+ void *reg, *mem; /* ptr to register/memory operand */
+ unsigned int *pseg = NULL; /* segment for memory operand (NULL=default) */
+ u32 eflags;
+ int opsz_override = 0;
+
if ( !linearise_address((u16)regs->xcs, regs->eip, (unsigned long *)&eip) )
{
DPRINTK("Cannot linearise %04x:%08lx\n", regs->xcs, regs->eip);
}
/* Parse prefix bytes. We're basically looking for segment override. */
- for ( i = 0; i < 4; i++ )
+ for ( pb = eip; (pb - eip) < 4; pb++ )
{
- if ( get_user(b, &eip[i]) )
+ if ( get_user(b, pb) )
{
- DPRINTK("Fault while accessing byte %d of instruction\n", i);
+ DPRINTK("Fault while accessing byte %d of instruction\n", pb-eip);
return 0;
}
}
done_prefix:
- nextbyte = &eip[i+1];
- if ( !decode_effective_address(&nextbyte, ®, &mem, pseg, regs) )
+ pb++; /* skip opcode byte */
+ switch ( decode_effective_address(&pb, ®, &mem, pseg, regs) )
+ {
+ case DECODE_EA_FAILED:
+ return 0;
+ case DECODE_EA_FIXME:
goto undecodeable;
+ }
/* Only handle single-byte opcodes right now. Sufficient for MOV. */
- /*
- * XXX Now I see how this decode routine is panning out, it needs
- * refactoring. Lots of duplicated cruft in here...
- */
switch ( b )
{
case 0x88: /* movb r,r/m */
if ( __put_user(*(u8 *)reg, (u8 *)mem) )
goto page_fault_w;
- regs->eip += nextbyte - eip;
break;
case 0x89: /* movl r,r/m */
- if ( opsz_override )
- {
- if ( __put_user(*(u16 *)reg, (u16 *)mem) )
- goto page_fault_w;
- }
- else
- {
- if ( __put_user(*(u32 *)reg, (u32 *)mem) )
- goto page_fault_w;
- }
- regs->eip += nextbyte - eip;
+ if ( opsz_override ? __put_user(*(u16 *)reg, (u16 *)mem)
+ : __put_user(*(u32 *)reg, (u32 *)mem) )
+ goto page_fault_w;
break;
case 0x8a: /* movb r/m,r */
if ( __get_user(*(u8 *)reg, (u8 *)mem) )
goto page_fault_r;
- regs->eip += nextbyte - eip;
break;
case 0x8b: /* movl r/m,r */
- if ( opsz_override )
- {
- if ( __get_user(*(u16 *)reg, (u16 *)mem) )
- goto page_fault_r;
- }
- else
- {
- if ( __get_user(*(u32 *)reg, (u32 *)mem) )
- goto page_fault_r;
- }
- regs->eip += nextbyte - eip;
+ if ( opsz_override ? __get_user(*(u16 *)reg, (u16 *)mem)
+ : __get_user(*(u32 *)reg, (u32 *)mem) )
+ goto page_fault_r;
break;
case 0xc6: /* movb imm,r/m */
if ( reg != ®s->eax ) /* Reg == /0 */
goto undecodeable;
- if ( get_user(rb, nextbyte) )
- {
- DPRINTK("Fault while extracting immediate byte\n");
- return 0;
- }
- if ( __put_user(rb, (u8 *)mem) )
+ GET_IMM8;
+ if ( __put_user(ib, (u8 *)mem) )
goto page_fault_w;
- regs->eip += nextbyte - eip + 1;
break;
case 0xc7: /* movl imm,r/m */
if ( reg != ®s->eax ) /* Reg == /0 */
goto undecodeable;
if ( opsz_override )
{
- if ( get_user(rw, (u16 *)nextbyte) )
- {
- DPRINTK("Fault while extracting immediate word\n");
- return 0;
- }
- if ( __put_user(rw, (u16 *)mem) )
+ GET_IMM16;
+ if ( __put_user(iw, (u16 *)mem) )
goto page_fault_w;
- regs->eip += nextbyte - eip + 2;
}
else
{
- if ( get_user(rl, (u32 *)nextbyte) )
- {
- DPRINTK("Fault while extracting immediate longword\n");
- return 0;
- }
- if ( __put_user(rl, (u32 *)mem) )
+ GET_IMM32;
+ if ( __put_user(il, (u32 *)mem) )
goto page_fault_w;
- regs->eip += nextbyte - eip + 4;
}
break;
case 0x80: /* cmpb imm8,r/m */
if ( reg != ®s->edi ) /* Reg == /7 */
goto undecodeable;
- if ( get_user(rb, nextbyte) )
- {
- DPRINTK("Fault while extracting immediate byte\n");
- return 0;
- }
+ GET_IMM8;
if ( __get_user(mb, (u8 *)mem) )
goto page_fault_r;
__asm__ __volatile__ (
"cmpb %b1,%b2 ; pushf ; popl %0"
: "=a" (eflags)
- : "0" (rb), "b" (mb) );
+ : "0" (ib), "b" (mb) );
regs->eflags &= ~0x8d5; /* OF,SF,ZF,AF,PF,CF */
regs->eflags |= eflags & 0x8d5;
- regs->eip += nextbyte - eip + 1;
break;
case 0x81: /* cmpl imm32,r/m */
if ( reg != ®s->edi ) /* Reg == /7 */
goto undecodeable;
if ( opsz_override )
{
- if ( get_user(rw, (u16 *)nextbyte) )
- {
- DPRINTK("Fault while extracting immediate word\n");
- return 0;
- }
+ GET_IMM16;
if ( __get_user(mw, (u16 *)mem) )
goto page_fault_r;
__asm__ __volatile__ (
"cmpw %w1,%w2 ; pushf ; popl %0"
: "=a" (eflags)
- : "0" (rw), "b" (mw) );
- regs->eip += nextbyte - eip + 2;
+ : "0" (iw), "b" (mw) );
}
else
{
- if ( get_user(rl, (u32 *)nextbyte) )
- {
- DPRINTK("Fault while extracting immediate longword\n");
- return 0;
- }
+ GET_IMM32;
if ( __get_user(ml, (u32 *)mem) )
goto page_fault_r;
__asm__ __volatile__ (
"cmpl %1,%2 ; pushf ; popl %0"
: "=a" (eflags)
- : "0" (rl), "b" (ml) );
- regs->eip += nextbyte - eip + 4;
+ : "0" (il), "b" (ml) );
}
regs->eflags &= ~0x8d5; /* OF,SF,ZF,AF,PF,CF */
regs->eflags |= eflags & 0x8d5;
case 0x83: /* cmpl imm8,r/m */
if ( reg != ®s->edi ) /* Reg == /7 */
goto undecodeable;
- if ( get_user(rb, nextbyte) )
- {
- DPRINTK("Fault while extracting immediate byte\n");
- return 0;
- }
+ GET_IMM8;
if ( opsz_override )
{
- rw = (rb & 0x80) ? (rb | ~0xff) : rb;
+ iw = (u16)(s16)(s8)ib;
if ( __get_user(mw, (u16 *)mem) )
goto page_fault_r;
__asm__ __volatile__ (
"cmpw %w1,%w2 ; pushf ; popl %0"
: "=a" (eflags)
- : "0" (rw), "b" (mw) );
- regs->eip += nextbyte - eip + 2;
+ : "0" (iw), "b" (mw) );
}
else
{
- rl = (rb & 0x80) ? (rb | ~0xff) : rb;
+ il = (u32)(s32)(s8)ib;
if ( __get_user(ml, (u32 *)mem) )
goto page_fault_r;
__asm__ __volatile__ (
"cmpl %1,%2 ; pushf ; popl %0"
: "=a" (eflags)
- : "0" (rl), "b" (ml) );
+ : "0" (il), "b" (ml) );
}
regs->eflags &= ~0x8d5; /* OF,SF,ZF,AF,PF,CF */
regs->eflags |= eflags & 0x8d5;
- regs->eip += nextbyte - eip + 1;
break;
case 0x38: /* cmpb r,r/m */
case 0x3a: /* cmpb r/m,r */
: "0" ((b==0x38)?rb:mb), "b" ((b==0x38)?mb:rb) );
regs->eflags &= ~0x8d5; /* OF,SF,ZF,AF,PF,CF */
regs->eflags |= eflags & 0x8d5;
- regs->eip += nextbyte - eip;
break;
case 0x39: /* cmpl r,r/m */
case 0x3b: /* cmpl r/m,r */
}
regs->eflags &= ~0x8d5; /* OF,SF,ZF,AF,PF,CF */
regs->eflags |= eflags & 0x8d5;
- regs->eip += nextbyte - eip;
break;
default:
DPRINTK("Unhandleable opcode byte %02x\n", b);
goto undecodeable;
}
- perfc_incrc(emulations);
-
/* Success! */
+ regs->eip += pb - eip;
+ perfc_incrc(emulations);
return 1;
undecodeable:
- printk("Undecodable instruction %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
- "caused GPF(0) at %04x:%08lx\n",
- eip[0], eip[1], eip[2], eip[3],
- eip[4], eip[5], eip[6], eip[7],
- regs->xcs, regs->eip);
+ DPRINTK("Undecodable instruction %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
+ "caused GPF(0) at %04x:%08lx\n",
+ eip[0], eip[1], eip[2], eip[3],
+ eip[4], eip[5], eip[6], eip[7],
+ regs->xcs, regs->eip);
return 0;
page_fault_w: